from typing import Sequence

import numpy as np

from pynumpak.general import Func
from pynumpak.type import func_t, Interval


class Lagrange(Func):
    def __init__(self, nodes: Sequence, values: Sequence):
        self.n = len(nodes)
        assert len(values) == self.n
        self.nodes = nodes
        self.values = values
        self.coe = []
        for i in range(self.n):
            self.coe.append(self._lagrange_interp_helper(i))

    def _lagrange_interp_helper(self, j: int):
        """ helper function interpolates on node[j]"""
        assert 0 <= j <= self.n
        prod = 1
        for i in range(self.n):
            if i == j:
                continue
            prod *= 1 / (self.nodes[j] - self.nodes[i])
        return prod

    def compute(self, x: float):
        p = 0
        s = 0
        for i in range(self.n):
            if x == self.nodes[i]:
                return self.values[i]
            p += self.coe[i] * self.values[i] / (x - self.nodes[i])
            s += self.coe[i] / (x - self.nodes[i])
        return p / s


def lagrange_minres(func: func_t, ab: Interval, n: int = 4):
    a, b = ab
    assert a < b
    nodes = [a + (np.cos((2 * i + 1) * np.pi / (2 * n)) + 1) * (b - a) / 2 for i in range(n)]
    values = [func(nodes[i]) for i in range(n)]
    return Lagrange(nodes, values)
